package com.tanx.cqrs.event.store;

import com.tanx.cqrs.event.Event;
import com.tanx.cqrs.eventsourcing.snapshot.SnapshotBuildStrategy;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * mongodb事件存储库
 */
@Data
public class MongodbEventStoreRepositoryImpl<A, E extends Event> implements EventStoreRepository<A, E> {
    private String collectionName;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private SnapshotBuildStrategy<A> strategy;

    @SuppressWarnings("unchecked")
    @Override
    public List<E> findByCreateDateTimeAfterAndIdByCreateDateTimeAsc(Date createDateTime, Object id) {
        Query query = new Query(Criteria.where("aggregateId").is(id).and("createDateTime").gt(createDateTime)
                .and("status").ne(EventStatus.FINISH));
        query.with(new Sort(Sort.Direction.ASC, "createDateTime"));
        return mongoTemplate.find(query, MongoEvent.class, collectionName).parallelStream().map(o -> (E) o.getEvent()).collect(Collectors.toList());
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<E> findByIdByCreateDateTimeAsc(Object id) {
        Query query = new Query(Criteria.where("aggregateId").is(id)
                .and("status").ne(EventStatus.FINISH));
        query.with(new Sort(Sort.Direction.ASC, "createDateTime"));
        return mongoTemplate.find(query, MongoEvent.class, collectionName).parallelStream().map(o -> (E) o.getEvent()).collect(Collectors.toList());
    }

    @Override
    public boolean save(Event event) {
        MongoEvent mongoEvent = new MongoEvent(event);
        mongoTemplate.insert(mongoEvent, collectionName);
        return true;
    }

    @Override
    public boolean delete(Event event) {
//        Query id = new Query(Criteria.where("_id").is(event.getUuid()));
//        DeleteResult remove = mongoTemplate.remove(id, collectionName);
//        long deletedCount = remove.getDeletedCount();
//        return deletedCount > 0;

        Query id = new Query(Criteria.where("_id").is(event.getUuid()));
        mongoTemplate.remove(id, collectionName);
        return true;
    }

    @Override
    public void finishEvent(String uuid) {
        Query query = new Query(Criteria.where("_id").is(uuid));
        mongoTemplate.updateFirst(query, Update.update("status", EventStatus.FINISH), MongoEvent.class, collectionName);
    }

    @SuppressWarnings("unchecked")
    @Override
    public E findByEventId(String uuid) {
        Query query = new Query(Criteria.where("_id").is(uuid));
        MongoEvent one = mongoTemplate.findOne(query, MongoEvent.class, collectionName);
        if (one == null) {
            return null;
        }
        return (E) one.getEvent();
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Event> findByUnFinishEventByCreateDateTimeAsc() {
        Query query = new Query(Criteria.where("status").is(EventStatus.UNFINISH));
        query.with(new Sort(Sort.Direction.ASC, "createDateTime"));
        List<MongoEvent> list = mongoTemplate.find(query, MongoEvent.class, collectionName);
        List<Event> result = new ArrayList<>(list.size());
        for (MongoEvent mongoEvent : list) {
            result.add(mongoEvent.getEvent());
        }
        return result;
    }

    @Override
    public SnapshotBuildStrategy<A> getSnapshotBuildStrategy() {
        return strategy;
    }
}
